package com.boot.down.controller;

import com.boot.down.service.RedPaperService;
import com.boot.down.service.UserRedPaperService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import redis.clients.jedis.Jedis;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/userRedPaper")
public class UserRedPaperController {

    @Autowired
    private UserRedPaperService userRedPaperService = null;

    @Autowired
    private RedPaperService redPaperService = null;

    @Autowired
    private RedisTemplate redisTemplate = null;

    /**
     * 实现方法
     * 1. 红包是20万元，个数为2万个，每个10元
     * 2. 3万人同时抢红包，如果数据库没做任何处理，比如加锁之类的，或出现 超出 / 负数的现象
     * 原因：
     * 1. 多线程情况下数据不一致造成的
     * 解决方法：
     * 1. 数据库的悲观锁，（对更新数据加锁，保证一旦事物持有数据记录的锁，其他线程将不再更新）
     *    缺点，容易导致大量线程挂起，等待，占用CPU等，浪费资源
     *
     * 2. 乐观锁 （不阻塞线程，不使用锁的机制）
     *    原理： CAS原理 （先保存旧值， --> 进入逻辑处理  --> 判断共享数据的值 是否等于 旧值
     *    --> 如果一致，更新数据； 如果不一致，表示数据被其他线程修改过，进入重试或放弃）
     *
     *    缺点， （1）容易产生ABA问题。-->
     *    1. 一个线程执行X=A并且存入旧值，另一个线程同理存入旧值；
     *    2. 此时第一个线程进入逻辑， 第二个线程开始执行X=B的修改操作，并执行逻辑一，然后继续还原
     *       X=A的值；
     *    3. 此时第一个线程因为判断的是X=A，执行更新数据（内部逻辑可能混乱，不知道用的是X=A或X=B）
     *       同时第二个线程执行逻辑二；
     *    4. 然后更新数据。
     *            解决方式： 数据库中加上冗余字段 用于判断是否更新， 如version 每次更新 + 1, 并且
     *            查询时根据查询的旧值进行判断是否更新。
     *
     *           （2）请求的失败率很高（解决： 加入重入锁（一种循环机制）, 也可以直接 加个for循环），
     *            但是，这会对数据库的性能要求很高
     *
     * 3. redis缓存Lua脚本实现，速度极快
     *    首先是秒及响应，
     * @param redId
     * @param userId
     * @return
     */

    @RequestMapping(value = "/addRedPaper")
    @ResponseBody
    public Map<String, Object> addRedPaper(Long redId, Long userId){

        //int result = userRedPaperService.addUserRedPaper(redId, userId);

        //int result = userRedPaperService.addUserRedPaperHappyLock(redId, userId);

        long result = userRedPaperService.grapUserRedPaperByRedis(redId, userId);
        Map<String, Object> retMap = new HashMap<>();

        boolean flag = result > 0;

        retMap.put("success", flag);

        retMap.put("message", flag ? "抢红包成功" : "抢红包失败");

        return  retMap;

    }

    @GetMapping("/list")
    public ModelAndView list(){

        return new ModelAndView("/main/index");
    }

    @GetMapping("/test/lua")
    @ResponseBody
    public String testRedisLua(){

        Jedis jedis = (Jedis) redisTemplate.getConnectionFactory().getConnection().getNativeConnection();

        String helloJava = (String) jedis.eval("return 'hello world'");

        jedis.eval("redis.call('set', KEYS[1], ARGV[1])", 1, "lua-key1", "lua-values");

        String luaKey = jedis.get("lua-key1");

        String sha1 = jedis.scriptLoad("redis.call('set', KEYS[1], ARGV[1])");

        jedis.evalsha(sha1,1, "keys-xiao","keys-values");

        String shaVal = jedis.get("keys-xiao");

        System.out.println(helloJava);

        return helloJava + " === " + luaKey + "------" +shaVal;
    }

    @RequestMapping("/create/red")
    @ResponseBody
    public int initRedPaper(Integer smallSupCount, BigDecimal smallPrice){

        return redPaperService.initRedPaper(smallSupCount, smallPrice);
    }
}
